﻿
#include<iostream>
using namespace std;


//1.多态的概念
//静态多态：编译时就确定调用哪个函数，固定方程式
//动态多态：运行时确定，虽然调用同一个函数，由于传入类名不同，也会返回不同方程式推导的结果
//(忘了问ai)



//2.多态的定义及概念

//2.1构成多态的三个条件
//继承关系：B 公有继承自 A									//必须存在类的继承层次结构
//虚函数重写：B 中的 func 重写了 A 中的虚函数 func			//基类中必须声明虚函数，派生类中要重写这个虚函数
//基类指针 / 引用调用：通过基类 A 的接口调用（在 test() 中）//通过基类的指针或引用来调用虚函数

//2.2虚函数，类成员函数前⾯加virtual修饰
//class Person
//{
//public:
//	virtual void BuyTicket() { cout << "买票-全价" << endl; }
//};

//2.3虚函数的重写/覆盖
//class Animal
//{
//public:
//	virtual void talk() const
//	{
//		printf("动物叫\n");
//	}
//};
//
//class Dog :public Animal
//{
//public:
//	virtual void talk() const
//	{
//		printf("汪汪\n");
//	}
//};
//
//class Cat :public Animal
//{
//public:
//	virtual void talk() const
//	{
//		printf("喵喵\n");
//	}
//};
//
//void LetsHear(Animal& animal)
//{//可以检索子类，换成子类Cat或Dog会报错
//	animal.talk();
//}
//
//int main()
//{
//	Dog dog;
//	Cat cat;
//
//	LetsHear(dog);
//	LetsHear(cat);
//
//	return 0;
//}

//2.4题目
//class A
//{
//public:
//	virtual void func(int val = 1) { std::cout << "A->" << val << std::endl; }
//	//虚函数		//val默认参数
//	virtual void test() { func(); }//这里调用func()使用的A的默认参数1
//};
//
//class B : public A
//{
//public:
//	void func(int val = 0) { std::cout << "B->" << val << std::endl; }
//	// 重写虚函数，但有新的默认参数val=0
//	// 注意：默认参数是静态绑定的，不会覆盖A的默认参数
//};
//
//int main()
//{
//	B* p = new B;
//
//	p->test();  // 输出 B->1
//	// 解释：test()继承自A，在其中调用func()使用A的默认参数1
//	// 但由于多态，实际调用的是B::func(1)
//
//	p->func();  // 输出 B->0  
//	// 解释：通过B*直接调用，使用B的默认参数0，不构成多态（不满足第三条，通过基类的指针或引用来调用虚函数）
//
//	return 0;
//}
//B->1


//2.5协变(了解)
//派⽣类重写基类虚函数时，与基类虚函数返回值类型不同。
//即基类虚函数返回基类对象的指针或者引⽤，派⽣类虚函数返回派⽣类对象的指针或者引⽤时，称为协变。
//(自己的看法)就是说派生类在重写虚函数时，使虚函数返回类型改变了，这种现象叫做协变，是会被认可的

//析构函数的重写，基类中的析构函数建议设计为虚函数
//class A
//{
//public:
//	virtual ~A()
//	{
//		cout << "~A()" << endl;
//	}
//};
//class B : public A {
//public:
//	~B()
//	{//这里析构函数名字与A不同
////虽然基类与派⽣类析构函数名字不同看起来不符合重写的规则，
////实际上编译器对析构函数的名称做了特殊处理，
////编译后析构函数的名称统⼀处理成destructor，
////所以基类的析构函数加了vialtual修饰，派⽣类的析构函数就构成重写
//		cout << "~B()->delete:" << _p << endl;
//		delete[] _p;
//	}
//protected:
//	int* _p = new int[10];
//};
//int main()
//{
//	A* p1 = new A;
//	A* p2 = new B;
//	delete p1;
//	//直接调用A类析构
//	delete p2;
//	//先调用B类析构，再调用A类析构
//	//如果是基类析构函数不是虚函数，只会调用B类析构，会导致资源泄露等（还会报错终止进程）
//
//	return 0;
//}


//2.6	override和final关键字
//override:检测是否重写，没重写会报错
//final:禁止重写，重写会报错

//2.7重载/重写/隐藏的对⽐
//重载：在同一个作用域中，函数名相同但参数列表不同（类型、数量、顺序），编译时根据参数决定调用哪个函数
//重写：在继承关系中，派生类函数与基类虚函数具有完全相同的函数签名，运行时根据对象实际类型决定调用哪个函数
//隐藏：在继承关系中，派生类函数与基类函数同名，导致基类同名函数在派生类作用域中被隐藏



//3.纯虚函数和抽象类
//在虚函数的后⾯写上 = 0，则这个函数为纯虚函数，纯虚函数不需要定义实现(实现没啥意义因为要被
//派⽣类重写，但是语法上可以实现)，只要声明即可。包含纯虚函数的类叫做抽象类，抽象类不能实例
//化出对象，如果派⽣类继承后不重写纯虚函数，那么派⽣类也是抽象类。纯虚函数某种程度上强制了
//派⽣类重写虚函数，因为不重写实例化不出对象。



//4.多态的原理
//4.1虚函数表指针
//⼀个含有虚函数的类中都⾄少都有⼀个虚函数表指针，因为⼀个类所有虚函数的地址要
//被放到这个类对象的虚函数表中，虚函数表也简称虚表
class Base
{
public:
	virtual void Func1()//4
	{
		cout << "Func1()" << endl;
	}
protected:
	int _b = 1;//4
	char _ch = 'x';//1
};
int main()
{
	Base b;
	cout << sizeof(b) << endl;
	return 0;
}
//编译为32位程序的运⾏结果是12


//4.2多态的实现核心是：
//	1.虚函数表：每个类一个，存储函数指针
//	2.vtable指针：每个对象一个，指向实际类型的vtable
//	3.动态查找：运行时通过vptr找到vtable，再找到函数地址


//4.3动态绑定和静态绑定(类似静态多态和动态多态)


//4.4我对于虚函数表的理解：
//	1.每个类都有各自独立的虚函数表
//	2.虚函数表本质是一个存虚函数指针的指针数组
//	3.基类虚函数表里的指针包括基类虚函数指针，
//	  派生类虚函数表包括继承的基类虚函数指针(未重写)、自身的虚函数指针、和重写后的虚函数指针


